using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TerritoryGame.Application.Interfaces;
using TerritoryGame.Domain.Entities.App;

namespace TerritoryGame.Application.Services
{
    public class AppGameStatsService : IAppGameStatsService
    {
        private readonly Dictionary<string, PlayerStats> _playerStats = new();
        private readonly Dictionary<string, RoomStats> _roomStats = new();
        private readonly List<GameRecord> _gameRecords = new();
        private readonly object _lock = new object();

        public async Task<PlayerStats> GetPlayerStatsAsync(string playerId)
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                if (_playerStats.TryGetValue(playerId, out var stats))
                {
                    return stats;
                }
                
                // 返回默认统计
                return new PlayerStats
                {
                    Id = Guid.Parse(playerId),
                    PlayerId = Guid.Parse(playerId),
                    Username = "Unknown",
                    TotalGamesPlayed = 0,
                    TotalGamesWon = 0,
                    TotalAreaPainted = 0,
                    AverageAreaPerGame = 0,
                    TotalPlayTime = TimeSpan.Zero,
                    FirstGameDate = DateTime.MinValue,
                    LastGameDate = DateTime.MinValue,
                    CurrentStreak = 0,
                    BestStreak = 0
                };
            }
        }

        public async Task<RoomStats> GetRoomStatsAsync(string roomId)
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                if (_roomStats.TryGetValue(roomId, out var stats))
                {
                    return stats;
                }
                
                return new RoomStats
                {
                    RoomId = roomId,
                    RoomName = "Unknown",
                    TotalGamesPlayed = 0,
                    TotalPlayers = 0,
                    AverageGameDuration = 0.0,
                    LastGameDate = DateTime.MinValue
                };
            }
        }

        public async Task<GlobalStats> GetGlobalStatsAsync()
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                var totalGames = _gameRecords.Count;
                var totalPlayers = _playerStats.Count;
                var totalRooms = _roomStats.Count;
                var totalArea = _playerStats.Values.Sum(p => p.TotalAreaPainted);
                var avgDuration = totalGames > 0 ? _gameRecords.Average(g => g.Duration) : 0.0;
                var lastGame = totalGames > 0 ? _gameRecords.Max(g => g.EndedAt) : DateTime.MinValue;

                return new GlobalStats
                {
                    TotalGamesPlayed = totalGames,
                    TotalPlayers = totalPlayers,
                    TotalRoomsCreated = totalRooms,
                    AverageGameDuration = avgDuration,
                    TotalAreaPainted = totalArea,
                    LastGameDate = lastGame
                };
            }
        }

        public async Task<List<LeaderboardEntry>> GetLeaderboardAsync(int top = 10)
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                return _playerStats.Values
                    .OrderByDescending(p => p.TotalGamesWon)
                    .ThenByDescending(p => p.WinRate)
                    .ThenByDescending(p => p.TotalAreaPainted)
                    .Take(top)
                    .Select((p, index) => new LeaderboardEntry
                    {
                        PlayerId = p.PlayerId,
                        Username = p.Username,
                        Rank = index + 1,
                        TotalGamesWon = p.TotalGamesWon,
                        TotalAreaPainted = p.TotalAreaPainted,
                        WinRate = p.WinRate,
                        CurrentStreak = p.CurrentStreak,
                        BestStreak = p.BestStreak,
                        LastGameDate = p.LastGameDate
                    })
                    .ToList();
            }
        }

        public async Task<List<LeaderboardEntry>> GetLeaderboardByCategoryAsync(string category, int top = 10)
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                var players = category.ToLower() switch
                {
                    "wins" => _playerStats.Values.OrderByDescending(p => p.TotalGamesWon),
                    "winrate" => _playerStats.Values.OrderByDescending(p => p.WinRate),
                    "area" => _playerStats.Values.OrderByDescending(p => p.TotalAreaPainted),
                    "streak" => _playerStats.Values.OrderByDescending(p => p.BestStreak),
                    _ => _playerStats.Values.OrderByDescending(p => p.TotalGamesWon)
                };

                return players
                    .Take(top)
                    .Select((p, index) => new LeaderboardEntry
                    {
                        PlayerId = p.PlayerId,
                        Username = p.Username,
                        Rank = index + 1,
                        TotalGamesWon = p.TotalGamesWon,
                        TotalAreaPainted = p.TotalAreaPainted,
                        WinRate = p.WinRate,
                        CurrentStreak = p.CurrentStreak,
                        BestStreak = p.BestStreak,
                        LastGameDate = p.LastGameDate
                    })
                    .ToList();
            }
        }

        public async Task UpdatePlayerStatsAsync(string playerId, GameRecord gameRecord)
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                if (!_playerStats.TryGetValue(playerId, out var stats))
                {
                    stats = new PlayerStats
                    {
                        Id = Guid.Parse(playerId),
                        PlayerId = Guid.Parse(playerId),
                        Username = gameRecord.PlayerResults.FirstOrDefault(p => p.PlayerId.ToString() == playerId)?.Username ?? "Unknown",
                        TotalGamesPlayed = 0,
                        TotalGamesWon = 0,
                        TotalAreaPainted = 0,
                        AverageAreaPerGame = 0,
                        TotalPlayTime = TimeSpan.Zero,
                        FirstGameDate = DateTime.MinValue,
                        LastGameDate = DateTime.MinValue,
                        CurrentStreak = 0,
                        BestStreak = 0
                    };
                    _playerStats[playerId] = stats;
                }

                // 更新统计信息
                stats.TotalGamesPlayed++;
                stats.LastGameDate = gameRecord.EndedAt;
                
                if (stats.FirstGameDate == DateTime.MinValue)
                {
                    stats.FirstGameDate = gameRecord.StartedAt;
                }

                var playerResult = gameRecord.PlayerResults.FirstOrDefault(p => p.PlayerId.ToString() == playerId);
                if (playerResult != null)
                {
                    stats.TotalAreaPainted += playerResult.AreaCount;
                    stats.AverageAreaPerGame = (double)stats.TotalAreaPainted / stats.TotalGamesPlayed;

                    if (playerResult.IsWinner)
                    {
                        stats.TotalGamesWon++;
                        stats.CurrentStreak++;
                        if (stats.CurrentStreak > stats.BestStreak)
                        {
                            stats.BestStreak = stats.CurrentStreak;
                        }
                    }
                    else
                    {
                        stats.CurrentStreak = 0;
                    }
                }
            }
        }

        public async Task UpdateRoomStatsAsync(string roomId, GameRecord gameRecord)
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                if (!_roomStats.TryGetValue(roomId, out var stats))
                {
                    stats = new RoomStats
                    {
                        RoomId = roomId,
                        RoomName = gameRecord.RoomName,
                        TotalGamesPlayed = 0,
                        TotalPlayers = 0,
                        AverageGameDuration = 0,
                        LastGameDate = DateTime.MinValue
                    };
                    _roomStats[roomId] = stats;
                }

                stats.TotalGamesPlayed++;
                stats.LastGameDate = gameRecord.EndedAt;
                stats.TotalPlayers = Math.Max(stats.TotalPlayers, gameRecord.PlayerCount);
                
                var totalDuration = _gameRecords
                    .Where(g => g.RoomId.ToString() == roomId)
                    .Sum(g => g.Duration);
                stats.AverageGameDuration = totalDuration / (double)stats.TotalGamesPlayed;
            }
        }

        public async Task<List<GameRecord>> GetTopGamesAsync(int top = 10)
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                return _gameRecords
                    .OrderByDescending(g => g.EndedAt)
                    .Take(top)
                    .ToList();
            }
        }

        public async Task<Dictionary<string, int>> GetPlayerRankingsAsync()
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                return _playerStats.Values
                    .OrderByDescending(p => p.TotalGamesWon)
                    .ThenByDescending(p => p.TotalAreaPainted)
                    .Select((p, index) => new { p.PlayerId, Rank = index + 1 })
                    .ToDictionary(x => x.PlayerId.ToString(), x => x.Rank);
            }
        }

        public async Task<Dictionary<string, int>> GetRoomRankingsAsync()
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                return _roomStats.Values
                    .OrderByDescending(r => r.TotalGamesPlayed)
                    .ThenByDescending(r => r.TotalPlayers)
                    .Select((r, index) => new { r.RoomId, Rank = index + 1 })
                    .ToDictionary(x => x.RoomId, x => x.Rank);
            }
        }

        public async Task<List<PlayerStats>> GetTopPlayersAsync(int top = 10)
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                return _playerStats.Values
                    .OrderByDescending(p => p.TotalGamesWon)
                    .ThenByDescending(p => p.WinRate)
                    .ThenByDescending(p => p.TotalAreaPainted)
                    .Take(top)
                    .ToList();
            }
        }

        public async Task<List<RoomStats>> GetTopRoomsAsync(int top = 10)
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                return _roomStats.Values
                    .OrderByDescending(r => r.TotalGamesPlayed)
                    .ThenByDescending(r => r.TotalPlayers)
                    .Take(top)
                    .ToList();
            }
        }

        public async Task<PlayerStats> GetPlayerStatsByDateRangeAsync(string playerId, DateTime startDate, DateTime endDate)
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                var gamesInRange = _gameRecords
                    .Where(g => g.EndedAt >= startDate && g.EndedAt <= endDate)
                    .Where(g => g.PlayerResults.Any(p => p.PlayerId.ToString() == playerId))
                    .ToList();

                var stats = new PlayerStats
                {
                    Id = Guid.Parse(playerId),
                    PlayerId = Guid.Parse(playerId),
                    Username = _playerStats.TryGetValue(playerId, out var existingStats) ? existingStats.Username : "Unknown",
                    TotalGamesPlayed = gamesInRange.Count,
                    TotalGamesWon = gamesInRange.Count(g => g.PlayerResults.Any(p => p.PlayerId.ToString() == playerId && p.IsWinner)),
                    TotalAreaPainted = gamesInRange.Sum(g => g.PlayerResults.FirstOrDefault(p => p.PlayerId.ToString() == playerId)?.AreaCount ?? 0),
                    AverageAreaPerGame = 0,
                    TotalPlayTime = TimeSpan.Zero,
                    FirstGameDate = gamesInRange.Any() ? gamesInRange.Min(g => g.StartedAt) : DateTime.MinValue,
                    LastGameDate = gamesInRange.Any() ? gamesInRange.Max(g => g.EndedAt) : DateTime.MinValue,
                    CurrentStreak = 0,
                    BestStreak = 0
                };

                if (stats.TotalGamesPlayed > 0)
                {
                    stats.AverageAreaPerGame = (double)stats.TotalAreaPainted / stats.TotalGamesPlayed;
                }

                return stats;
            }
        }

        public async Task<RoomStats> GetRoomStatsByDateRangeAsync(string roomId, DateTime startDate, DateTime endDate)
        {
            await Task.CompletedTask;
            
            lock (_lock)
            {
                var gamesInRange = _gameRecords
                    .Where(g => g.RoomId.ToString() == roomId && g.EndedAt >= startDate && g.EndedAt <= endDate)
                    .ToList();

                var stats = new RoomStats
                {
                    RoomId = roomId,
                    RoomName = _roomStats.TryGetValue(roomId, out var existingStats) ? existingStats.RoomName : "Unknown",
                    TotalGamesPlayed = gamesInRange.Count,
                    TotalPlayers = gamesInRange.Any() ? gamesInRange.Max(g => g.PlayerCount) : 0,
                    AverageGameDuration = gamesInRange.Any() ? gamesInRange.Average(g => g.Duration) : 0.0,
                    LastGameDate = gamesInRange.Any() ? gamesInRange.Max(g => g.EndedAt) : DateTime.MinValue
                };

                return stats;
            }
        }
    }
}
